package com.dev.ac.aspect;

import com.dev.ac.annotation.NoRepeatSubmit;
import com.dev.ac.common.Result;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/**
 * @Author 付
 * @Date 2024-04-18 13:38
 * @Version 1.0
 * @description 切面类，防止重复提交
 * @since 1.0
 */
@Slf4j
@Component
@Aspect
public class RepeatSubmitAspect {
	@Autowired
	RedissonClient redissonClient;

	@Pointcut("@annotation(noRepeatSubmit)")
	public void pointCut(NoRepeatSubmit noRepeatSubmit) {
	}

	@Around("pointCut(noRepeatSubmit)")
	public Object around(ProceedingJoinPoint point, NoRepeatSubmit noRepeatSubmit) throws Throwable {
		// 这里不能直接获取request，需要转型获取
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = requestAttributes.getRequest();
		String token = request.getHeader("token");
		String path = request.getServletPath();
		String key = token + path;

		// 获取锁，这里使用的是可重入锁
		RLock lock = redissonClient.getLock(key);
		/**
		 * 上锁
		 *  waitTime 上锁时间，这里是 0 ，意味着当请求时立即上锁
		 *  leaseTime 锁持续时间，这里时获取注解里面的方法设置的时间，当时间到了，锁释放
		 *  timeUnit 时间单位，这里时秒级别
		 * 不传递参数默认值是：不等待（-1），30秒
		 */
		boolean tryLock = lock.tryLock(0, noRepeatSubmit.lockTime(), TimeUnit.SECONDS);

		if (!tryLock) {
			return Result.error("重复请求，请稍后再试！");
		}

		// 上锁成功，执行切点方法
		return point.proceed();
	}
}
